/*
* Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "napi_init.h"
#include "napi_utils.h"
#include "MWServer.h"
#include <napi/native_api.h>
#include <memory>
#include <map>

using namespace OHOS::OHMW;

struct MWServerWrap {
    std::shared_ptr<MWServer> mwServer;
    napi_ref object_ref;
};

std::map<MWServer::Type, std::shared_ptr<struct MWServerWrap>> g_server_wraps;

void FinalizeMWServer(napi_env env, void *data, void *hint) {
    struct MWServerWrap *serverWrap = static_cast<struct MWServerWrap *>(data);
    for (auto iter = g_server_wraps.begin(); iter != g_server_wraps.end(); iter++) {
        if (iter->second.get() == serverWrap) {
            napi_delete_reference(env, serverWrap->object_ref);
            serverWrap->mwServer.reset();
            g_server_wraps.erase(iter);
            break;
        }
    }
}

napi_value MWServerConstructor(napi_env env, napi_callback_info info) {
    NAPI_CHECKOUT_ARGS(1);

    NAPI_CHECKOUT_INT32(type, args[0]);
    MWServer::Type serverType = static_cast<MWServer::Type>(type);
    if (serverType < MWServer::Type::DORA || serverType > MWServer::Type::ROS2) {
        CLOGE("Invalid server type");
        return nullptr;
    }
    napi_status _status;
    if (g_server_wraps.find(serverType) != g_server_wraps.end()) {
        napi_value returnValue;
        std::shared_ptr<struct MWServerWrap> serverWrap = g_server_wraps[serverType];
        _status = napi_get_reference_value(env, serverWrap->object_ref, &returnValue);
        NAPI_CHECK_STATUS("new MWServer napi_get_reference_value failed");
        CLOGI("MWServerConstructor return existing instance");
        return returnValue;
    } else {
        std::shared_ptr<struct MWServerWrap> serverWrap = std::make_shared<struct MWServerWrap>();
        serverWrap->mwServer = std::make_shared<MWServer>(serverType);
        _status =
            napi_wrap(env, thisVar, reinterpret_cast<void *>(serverWrap.get()), FinalizeMWServer, nullptr, nullptr);
        NAPI_CHECK_STATUS("new MWServer napi_wrap failed");
        _status = napi_create_reference(env, thisVar, 1, &serverWrap->object_ref);
        NAPI_CHECK_STATUS("new MWServer napi_create_reference failed");
        g_server_wraps[serverType] = serverWrap;
        CLOGI("MWServerConstructor created new instance");
        return thisVar;
    }
}

napi_value MWServerStart(napi_env env, napi_callback_info info) {
    napi_value thisVar;
    napi_get_cb_info(env, info, 0, nullptr, &thisVar, nullptr);

    struct MWServerWrap *serverWrap;
    napi_unwrap(env, thisVar, reinterpret_cast<void **>(&serverWrap));
    CLOGI("calling mwServer->Start()");
    int32_t result = serverWrap->mwServer->Start();

    NAPI_PACKAGE_BOOL(ret, result == 0);
    return ret;
}

napi_value MWServerStop(napi_env env, napi_callback_info info) {
    napi_value thisVar;
    napi_get_cb_info(env, info, 0, nullptr, &thisVar, nullptr);

    struct MWServerWrap *serverWrap;
    napi_unwrap(env, thisVar, reinterpret_cast<void **>(&serverWrap));
    CLOGI("calling mwServer->Stop()");
    int32_t result = serverWrap->mwServer->Stop();

    NAPI_PACKAGE_BOOL(ret, result == 0);
    return ret;
}

napi_value MWServerIsRunning(napi_env env, napi_callback_info info) {
    NAPI_CHECKOUT_ARGS(1);
    NAPI_CHECKOUT_INT32(type, args[0]);
    MWServer::Type serverType = static_cast<MWServer::Type>(type);
    NAPI_PACKAGE_BOOL(ret, MWServer::IsRunning(serverType));
    return ret;
}

napi_value OhmwNapiMWServerInit(napi_env env, napi_value exports) {
    napi_property_descriptor properties[] = {
        {"start", nullptr, MWServerStart, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"stop", nullptr, MWServerStop, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"isRunning", nullptr, MWServerIsRunning, nullptr, nullptr, nullptr, napi_default, nullptr}};

    napi_value cons;
    napi_define_class(env, "MWServer", NAPI_AUTO_LENGTH, MWServerConstructor, nullptr,
        sizeof(properties) / sizeof(napi_property_descriptor), properties, &cons);
    napi_set_named_property(env, exports, "MWServer", cons);

    napi_value enumObj;
    napi_create_object(env, &enumObj);
    napi_value dora, ros, ros2;
    napi_create_int32(env, static_cast<int32_t>(MWServer::Type::DORA), &dora);
    napi_create_int32(env, static_cast<int32_t>(MWServer::Type::ROS), &ros);
    napi_create_int32(env, static_cast<int32_t>(MWServer::Type::ROS2), &ros2);
    napi_set_named_property(env, enumObj, "DORA", dora);
    napi_set_named_property(env, enumObj, "ROS", ros);
    napi_set_named_property(env, enumObj, "ROS2", ros2);
    napi_set_named_property(env, exports, "MWServerType", enumObj);

    return exports;
}
